//
//  BCAppMonitor.m
//  BCDebugKit
//
//  Created by Basic on 2021/3/1.
//

#import "BCDebugAppMonitor.h"
#import "BCDebugKitPublic.h"
#import <mach/mach.h>
#import <sys/sysctl.h>


@interface BCDebugAppMonitor()
@property (nonatomic, assign, readwrite) NSInteger fps;
@property (nonatomic, assign, readwrite) NSInteger fpsCount;
@property (nonatomic, assign, readwrite) CGFloat ram;
@property (nonatomic, assign, readwrite) NSInteger cpu;
/// app状态是否可用，退后台不可用
@property (nonatomic, assign) BOOL appActive;
/// 是否启用fps监测
@property (nonatomic, assign) BOOL enableFps;
/// 是否启用ram监测
@property (nonatomic, assign) BOOL enableRam;
/// 是否启用cpu监测
@property (nonatomic, assign) BOOL enableCpu;
@property (nonatomic, strong) CADisplayLink *link;
@property (nonatomic, assign) NSTimeInterval lastTime;
@property (nonatomic, assign, readwrite) long startTime;
@end

@implementation BCDebugAppMonitor
#pragma mark - system
static BCDebugAppMonitor *kBCDebugAppMonitorInstance = nil;
+ (instancetype)sharedInstance {
    static dispatch_once_t kBCAppMonitorOnceToken;
    dispatch_once(&kBCAppMonitorOnceToken, ^{
        kBCDebugAppMonitorInstance = [[BCDebugAppMonitor alloc] init];
    });
    return kBCDebugAppMonitorInstance;
}
- (instancetype)init {
    self = [super init];
    if (self) {
        _appActive = true;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBack:) name:UIApplicationDidEnterBackgroundNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    }
    return self;
}
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

//MARK: - fps监测
- (void)startFpsMonitor {
    self.enableFps = true;
    [self startMonitorHandle];
}
- (void)stopFpsMonitor {
    self.enableFps = false;
    [self stopMonitor];
    [[NSNotificationCenter defaultCenter] postNotificationName:(NSNotificationName)BCDebugKit_ApmUpdate object:nil userInfo:nil];
}

//MARK: - ram监测
- (void)startRamMonitor {
    self.enableRam = true;
    [self startMonitorHandle];
}
- (void)stopRamMonitor {
    self.enableRam = false;
    [[NSNotificationCenter defaultCenter] postNotificationName:(NSNotificationName)BCDebugKit_ApmUpdate object:nil userInfo:nil];
    [self stopMonitor];
}

//MARK: - cpu监测
- (void)startCpuMonitor {
    self.enableCpu = true;
    [self startMonitorHandle];
}
- (void)stopCpuMonitor {
    self.enableCpu = false;
    [[NSNotificationCenter defaultCenter] postNotificationName:(NSNotificationName)BCDebugKit_ApmUpdate object:nil userInfo:nil];
    [self stopMonitor];
}

//MARK: - 统一监测
- (void)startMonitorHandle {
    if (!self.appActive) {
        return;
    }
    if (!self.enableFps && !self.enableRam && !self.enableCpu) {
        //没有监测项
        return ;
    }
    if (_link != nil && !_link.isPaused) {
        //已经启动
        return;
    }
    if (_link) {
        [_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        [_link invalidate];
        _link = nil;
    }
    _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(onMonitorHandle:)];
    [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)stopMonitor {
    if (self.appActive) {
        //app 状态可用，且有需要的检测项
        if (self.enableFps || self.enableRam || self.enableCpu) {
            return ;
        }
    }
    if (_link) {
        [_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        [_link invalidate];
        _link = nil;
    }
    self.lastTime = 0;
    self.fpsCount = 0;
}
- (void)onMonitorHandle:(CADisplayLink *)link {
    if (self.lastTime == 0) {
        self.lastTime = link.timestamp;
        return;
    }
    NSTimeInterval delta = link.timestamp - self.lastTime;
    if (delta >= 1) {
        self.lastTime = link.timestamp;
    }
    //计算fps
    BOOL needNotif = false;
    if (self.enableFps) {
        self.fpsCount++;
        if (delta >= 1) {
            self.fps = self.fpsCount / delta;
            needNotif = true;
            self.fpsCount = 0;
        }
    }
    //计算ram
    if (self.enableRam) {
        if (delta >= 1) {
            self.ram = [BCDebugAppMonitor getAppRAMUsage]/1024.0/1024.0;
            needNotif = true;
        }
    }
    //计算cpu
    if (self.enableCpu) {
        if (delta >= 1) {
            self.cpu = (NSInteger)[BCDebugAppMonitor getCpuUsage];
            needNotif = true;
        }
    }
    //apm信息通知
    if (needNotif) {
        NSMutableDictionary *parmas = [[NSMutableDictionary alloc] init];
        if (self.enableFps) {
            parmas[@"fps"] = @(self.fps);
        }
        if (self.enableRam) {
            parmas[@"ram"] = @(self.ram);
        }
        if (self.enableCpu) {
            parmas[@"cpu"] = @(self.cpu);
        }
        [[NSNotificationCenter defaultCenter] postNotificationName:(NSNotificationName)BCDebugKit_ApmUpdate object:nil userInfo:parmas];
    }
}

//MARK: - utils
/// 获取app使用内存
+ (int64_t)getAppRAMUsage {
    int64_t memoryUsageInByte = 0;
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if(kernelReturn == KERN_SUCCESS) {
        memoryUsageInByte = (int64_t) vmInfo.phys_footprint;
    }
    return memoryUsageInByte;
}
/// 获取cpu使用率
+ (float)getCpuUsage {
    kern_return_t           kr;
    thread_array_t          thread_list;
    mach_msg_type_number_t  thread_count;
    thread_info_data_t      thinfo;
    mach_msg_type_number_t  thread_info_count;
    thread_basic_info_t     basic_info_th;
    kr = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (kr != KERN_SUCCESS) {
        return -1;
    }
    float cpu_usage = 0;
    for (int i = 0; i < thread_count; i++) {
        thread_info_count = THREAD_INFO_MAX;
        kr = thread_info(thread_list[i], THREAD_BASIC_INFO,(thread_info_t)thinfo, &thread_info_count);
        if (kr != KERN_SUCCESS) {
            return -1;
        }
        basic_info_th = (thread_basic_info_t)thinfo;
        if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
            cpu_usage += basic_info_th->cpu_usage;
        }
    }
    cpu_usage = cpu_usage / (float)TH_USAGE_SCALE * 100.0;
    vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
    return cpu_usage;
}


//MARK: - 启动时长
/// 计算启动时长
- (void )loadAppStartTime {
    //进程创建时间
    struct kinfo_proc procInfo;
    int pid = [[NSProcessInfo processInfo] processIdentifier];
    int cmd[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    size_t size = sizeof(procInfo);
    double t1 = 0;
    if (sysctl(cmd, sizeof(cmd)/sizeof(*cmd), &procInfo, &size, NULL, 0) == 0) {
        t1 = procInfo.kp_proc.p_un.__p_starttime.tv_sec * 1000.0 + procInfo.kp_proc.p_un.__p_starttime.tv_usec / 1000.0;
    }
    //启动结束时间
    double t2 = CFAbsoluteTimeGetCurrent()*1000 + kCFAbsoluteTimeIntervalSince1970*1000;;
    self.startTime = (long)(t2 - t1);
}

//MARK: - notifcation
- (void)onAppDidBack:(NSNotification *)notif {
    self.appActive = false;
    [self stopMonitor];
}
- (void)onAppDidBecomeActive:(NSNotification *)notif {
    self.appActive = true;
    [self startMonitorHandle];
}

@end

//#ifdef DEBUG
//void static __attribute__((constructor)) before_main() {
//    if ([BCDebugAppMonitor sharedInstance].__t1 == 0) {
//        [BCDebugAppMonitor sharedInstance].__t1 = [BCDebugAppMonitor getProcessStartTime];
//        NSLog(@"t1:%lf", [BCDebugAppMonitor sharedInstance].__t1);
//    }
//    if ([BCDebugAppMonitor sharedInstance].__t2 == 0) {
//        [BCDebugAppMonitor sharedInstance].__t2 = CFAbsoluteTimeGetCurrent()*1000 + kCFAbsoluteTimeIntervalSince1970*1000;
//        NSLog(@"t2:%lf", [BCDebugAppMonitor sharedInstance].__t2);
//        NSLog(@"t2:%lf", [[NSDate date] timeIntervalSince1970]*1000);
//    }
//}
//#endif
